import { randomRangeInt } from "cc";
import { ActionQueue } from "./ActionQueue";
import { DataManager } from "./DataManager";
import { GameActionQueueEnum, GameManager } from "./GameManager";
import { SkillEnum } from "../data/skill/SkillEnum";

export enum PalBattleEventEnum {
    PushEnemy = 'PushEnemy',
    PushHero = 'PushHero',
    RemoveEnemy = 'RemoveEnemy',
    RemoveHero = 'RemoveHero',
    TurnChange = 'TurnChange',
    ChangeHero = 'ChangeHero',
    HitEnemy = 'HitEnemy',
    HitHero = 'HitHero',
    EnemyDead = 'EnemyDead',
    HeroDead = 'HeroDead',
    HeroStatusChange = 'HeroStatusChange',
    EnemyStatusChange = 'EnemyStatusChange',
    PushSkill = 'PushSkill',
}

export interface PalBattleSkillOriginData {
    skillID: number;
    name: string;
    mpCost: number;
    needTarget: boolean;
    order: () => number;
    targets: (args: { system: PalBattleSystem }) => string[];
    callable: (args: { game: GameManager; system: PalBattleSystem; targets: string[] }) => void;
}

export interface PalBattleSkillSaveData {
    UUID: string;
}

export interface PalBattleSkill {
    originData: PalBattleSkillOriginData;
    saveData: PalBattleSkillSaveData
}

export interface PalBattleEnemyOriginData {
    enemyID: number;
    name: string;
    hp: number;
    武术: number;
    灵力: number;
    防御: number;
    身法: number;
    吉运: number;
    ai: (args: any) => void;
}

export interface PalBattleEnemySaveData {
    UUID: string;
    hp: number;
    武术: number;
    灵力: number;
    防御: number;
    身法: number;
    吉运: number;
    dead: boolean; // 死亡
    defense: boolean; // 防御 -> 防御状态
    stun: boolean; // 眩晕 -> 回梦
}

export interface PalBattleEnemy {
    originData: PalBattleEnemyOriginData;
    saveData: PalBattleEnemySaveData;
}

export interface PalBattleHeroOriginData {
    heroID: number;
    name: string;
    hp: number;
    mp: number;
    武术: number;
    灵力: number;
    防御: number;
    身法: number;
    吉运: number;
    levelUp: {
        hp?: number;
        mp?: number;
        武术?: number;
        灵力?: number;
        防御?: number;
        身法?: number;
        吉运?: number;
    },
    setup: (args: {
        hero: PalBattleHero;
        system: PalBattleSystem;
    }) => void;
}

export interface PalBattleHeroSaveData {
    UUID: string;
    heroID: number;
    hp: number;
    mp: number;
    武术: number;
    灵力: number;
    防御: number;
    身法: number;
    吉运: number;
    level: number;
    exp: number;
    maxExp: number;
    dead: boolean; // 死亡
    stun: number; // 眩晕 -> 回梦
    defense: boolean; // 防御 -> 防御状态
    skills: number[]; // 技能的 枚举数字
}

export interface PalBattleHero {
    originData: PalBattleHeroOriginData;
    saveData: PalBattleHeroSaveData;
}

export class PalBattleSystem {
    events: {
        name: string;
        callable: (args: any) => void;
        content: any;
    }[] = [];
    on(name: string, callable: (args: any) => void, content?: any) {
        this.events.push({
            name,
            callable,
            content
        })
    }
    off(name: string, callable: (args: any) => void, content?: any) {
        const index = this.events.findIndex(event => {
            return event.name === name && event.callable === callable && event.content === content;
        });
        if (index !== -1) {
            this.events.splice(index, 1);
        }
    }
    emit(name: string, args: any, content?: any) {
        this.events.forEach(event => {
            if (event.name === name) {
                event.callable.call(event.content, args);
            }
        })
    }

    enemies: PalBattleEnemy[] = [];
    heros: PalBattleHero[] = [];
    turn: 'player' | 'enemy';
    currentHeroIndex: number = 0;
    actionQueue: ActionQueue;

    setTurn(turn: 'player' | 'enemy') {
        this.turn = turn;
        this.emit(PalBattleEventEnum.TurnChange, { turn });
        if (this.turn === 'player') {
            this.heros.forEach(hero => {
                this.clearHeroStatus(hero.saveData.UUID);
            })
            this.emit(PalBattleEventEnum.ChangeHero, { hero: this.heros[this.currentHeroIndex] });
        } else {
            this.enemies.forEach(enemy => {
                enemy.originData.ai({ enemy });
            })
            this.currentHeroIndex = 0;
            this.actionQueue.add({
                actions: [{
                    duration: 0,
                    action: GameActionQueueEnum.SetHeroTurn,
                    args: {}
                }],
                flag: ""
            });
            this.actionQueue.play();
        }
    }
    nextTurn() {
        this.turn = this.turn === 'player' ? 'enemy' : 'player';
        this.setTurn(this.turn);
    }
    nextHero() {
        this.currentHeroIndex++;
        this.emit(PalBattleEventEnum.ChangeHero, { hero: this.getCurrentHero() });
        if (this.currentHeroIndex >= this.heros.length) {
            this.setTurn('enemy');
        }
    }

    clearHeroStatus(UUID: string) {
        const hero = this.getHero(UUID);
        if (!hero) return;
        hero.saveData.defense = false;
        if (hero.saveData.stun > 0) hero.saveData.stun -= 1;
        this.actionQueue.add({
            actions: [
                {
                    action: GameActionQueueEnum.SetHeroStatus,
                    duration: 0,
                    args: {
                        UUID,
                        defense: hero.saveData.defense,
                        stun: hero.saveData.stun,
                    }
                }
            ],
            flag: ""
        });
        this.actionQueue.play();
    }

    setData(enemies: number[], heros: PalBattleHero[]) {
        enemies.forEach(enemyID => {
            const enemy = (DataManager.instance as DataManager).getEnemy(enemyID);
            this.pushEnemy(enemy);
        });
        heros.forEach(hero => {
            this.pushHero(hero);
        });
        setTimeout(() => {
            this.setTurn('player');
        }, 105);
    }
    getCurrentHero(): PalBattleHero {
        return this.heros[this.currentHeroIndex];
    }
    getRandomHero(): PalBattleHero {
        const heros = this.heros.filter(iter => !iter.saveData.dead);
        const randomIndex = randomRangeInt(0, heros.length);
        return heros[randomIndex];
    }
    getRandomEnemy(): PalBattleEnemy {
        const enemies = this.enemies.filter(iter => !iter.saveData.dead);
        const randomIndex = randomRangeInt(0, enemies.length);
        return enemies[randomIndex];
    }
    getHeroAtk(UUID: string) {
        return this.getHero(UUID)?.saveData.武术 || 0;
    }
    getEnemyAtk(UUID: string) {
        return this.getEnemy(UUID)?.originData.武术 || 0;
    }
    getHeroHp(UUID: string) {
        return this.getHero(UUID)?.saveData.hp || 0;
    }
    getHeroMaxHp(UUID: string) {
        return this.getHero(UUID)?.originData.hp || 0;
    }
    getEnemyHp(UUID: string) {
        return this.getEnemy(UUID)?.saveData.hp || 0;
    }
    getEnemyMaxHp(UUID: string) {
        return this.getEnemy(UUID)?.originData.hp || 0;
    }
    pushEnemy(enemy: PalBattleEnemy) {
        this.enemies.push(enemy);
        this.emit(PalBattleEventEnum.PushEnemy, { enemy }); // 事件传导 -》 只要是注册了 PushEnemy 事件的，都会被调用
    }
    pushHero(hero: PalBattleHero) {
        this.heros.push(hero);
        hero.originData.setup.call(this, {
            hero,
            system: this,
        });
        this.emit(PalBattleEventEnum.PushHero, { hero });
    }
    pushSkill(UUID: string, skill: SkillEnum) {
        const hero = this.getHero(UUID);
        if (!hero) return;
        // @ts-ignore
        if (hero.saveData.skills.includes(skill)) return;
        hero.saveData.skills.push(skill);
        this.emit(PalBattleEventEnum.PushSkill, { UUID, skill });
    }
    getEnemy(UUID: string) {
        return this.enemies.find(enemy => enemy.saveData.UUID === UUID);
    }
    getHero(UUID: string) {
        return this.heros.find(hero => hero.saveData.UUID === UUID);
    }
    getHeroDie(UUID: string) {
        return this.getHero(UUID)?.saveData.dead;
    }
    getEnemyDie(UUID: string) {
        return this.getEnemy(UUID)?.saveData.dead;
    }
    removeEnemy(UUID: string) {
        const index = this.enemies.findIndex(enemy => enemy.saveData.UUID === UUID);
        if (index !== -1) {
            this.enemies.splice(index, 1);
            this.emit(PalBattleEventEnum.RemoveEnemy, { UUID });
        }
    }
    removeHero(UUID: string) {
        const index = this.heros.findIndex(hero => hero.saveData.UUID === UUID);
        if (index !== -1) {
            this.heros.splice(index, 1);
            this.emit(PalBattleEventEnum.RemoveHero, { UUID });
        }
    }
    heroDie(UUID: string) {
        const hero = this.getHero(UUID);
        if (hero) {
            hero.saveData.dead = true;
            this.emit(PalBattleEventEnum.HeroDead, { UUID });
        }
    }
    enemyDie(UUID: string) {
        const enemy = this.getEnemy(UUID);
        if (enemy) {
            enemy.saveData.dead = true;
            this.emit(PalBattleEventEnum.EnemyDead, { UUID });
        }
    }
    setHeroStatus(UUID: string, status: string, value: any) {
        const hero = this.getHero(UUID);
        if (!hero) return;
        hero.saveData[status] = value;
        this.emit(PalBattleEventEnum.HeroStatusChange, { UUID, status, value });
        if (hero.saveData.hp <= 0) {
            this.heroDie(UUID);
        }
    }
    setEnemyStatus(UUID: string, status: string, value: any) {
        const enemy = this.getEnemy(UUID);
        if (!enemy) return;
        enemy.saveData[status] = value;
        this.emit(PalBattleEventEnum.EnemyStatusChange, { UUID, status, value });
        if (enemy.saveData.hp <= 0) {
            this.enemyDie(UUID);
        }
    }
    getHealHero(UUID: string, value: number): number {
        let hp = this.getHeroHp(UUID) + value;
        if (hp > this.getHeroMaxHp(UUID)) hp = this.getHeroMaxHp(UUID);
        return hp;
    }
    getHealEnemy(UUID: string, value: number): number {
        let hp = this.getEnemyHp(UUID) + value;
        if (hp > this.getEnemyMaxHp(UUID)) hp = this.getEnemyMaxHp(UUID);
        return hp;
    }
}


